﻿using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using S7.Net;
using S7PlcDataManager.Tools;

namespace S7PlcDataManager.S7Plc
{
    public class S7PlcHelper
    {
        public bool IsConnected => _plc?.IsConnected ?? false;
        public string Ip => _plc?.IP;
        public string LastErrorMsg { get; private set; }

        private Plc _plc;

        public void Init(CpuType cpu, string ip, int port, short rack, short slot)
        {
            _plc = new Plc(cpu, ip, port, rack, slot);
        }

        public void Init(CpuType cpu, string ip, short rack, short slot)
        {
            _plc = new Plc(cpu, ip, rack, slot);
        }

        public void UnInit()
        {
            (_plc as IDisposable)?.Dispose();
            _plc = null;
        }

        public async Task OpenAsync(CancellationToken cancellationToken)
        {
            if (_plc != null)
            {
                await _plc.OpenAsync(cancellationToken);
            }
        }

        public void Close()
        {
            _plc?.Close();
        }

        #region ReadVariable

        public bool ReadBitVariable(string variable, out bool val)
        {
            try
            {
                val = (bool) _plc.Read(variable);
                return true;
            }
            catch (Exception e)
            {
                val = false;
                LogHelper.AddErrorLog(e.Message);
                return false;
            }
        }

        public bool ReadByteVariable(string variable, out byte val)
        {
            try
            {
                val = (byte) _plc.Read(variable);
                return true;
            }
            catch (Exception e)
            {
                val = 0;
                LogHelper.AddErrorLog(e.Message);
                return false;
            }
        }

        public bool ReadWordVariable(string variable, out ushort val)
        {
            try
            {
                val = (ushort) _plc.Read(variable);
                return true;
            }
            catch (Exception e)
            {
                val = 0;
                LogHelper.AddErrorLog(e.Message);
                return false;
            }
        }

        public bool ReadDWordVariable(string variable, out uint val)
        {
            try
            {
                val = (ushort) _plc.Read(variable);
                return true;
            }
            catch (Exception e)
            {
                val = 0;
                LogHelper.AddErrorLog(e.Message);
                return false;
            }
        }

        public bool ReadFloatVariable(string variable, out float val)
        {
            try
            {
                val = (float) _plc.Read(variable);
                return true;
            }
            catch (Exception e)
            {
                val = 0;
                LogHelper.AddErrorLog(e.Message);
                return false;
            }
        }

        public bool ReadDoubleVariable(string variable, out double val)
        {
            try
            {
                val = (double) _plc.Read(variable);
                return true;
            }
            catch (Exception e)
            {
                val = 0;
                LogHelper.AddErrorLog(e.Message);
                return false;
            }
        }

        public bool ReadDWord2Variable(string variable, out uint val)
        {
            try
            {
                val = (ushort) _plc.Read(variable);
                return true;
            }
            catch (Exception e)
            {
                val = 0;
                LogHelper.AddErrorLog(e.Message);
                return false;
            }
        }

        #endregion

        #region General Read/Write

        public bool ReadVariable(string variable, out object val)
        {
            val = null;

            if (!_plc.IsConnected)
            {
                return false;
            }

            try
            {
                val = _plc.Read(variable);
                return true;
            }
            catch (Exception e)
            {
                LastErrorMsg = e.Message;
                LogHelper.AddErrorLog(LastErrorMsg);
                return false;
            }
        }

        public bool WriteVariable(string variable, object val)
        {
            if (!_plc.IsConnected)
            {
                return false;
            }

            try
            {
                _plc.Write(variable, val);
                return true;
            }
            catch (Exception e)
            {
                LastErrorMsg = e.Message;
                LogHelper.AddErrorLog(LastErrorMsg);
                return false;
            }
        }

        public bool ReadString(string variable, out string val)
        {
            val = string.Empty;

            if (!_plc.IsConnected)
            {
                return false;
            }

            DataType dataType;
            int dbNumber;
            int address;
            try
            {
                VarType varType;
                int bitNumber;
                S7PlcProtocol.Parse(variable, out dataType, out dbNumber, out varType, out address, out bitNumber);
            }
            catch (Exception e)
            {
                LastErrorMsg = $@"变量名称[{variable}]解析失败：{e.Message}";
                LogHelper.AddErrorLog(LastErrorMsg);
                return false;
            }

            try
            {
                var count = (byte) _plc.Read(dataType, dbNumber, address, VarType.Byte, 1);
                val = (string) _plc.Read(dataType, dbNumber, address + 1, VarType.String, count);
                return true;
            }
            catch (Exception e)
            {
                LastErrorMsg = e.Message;
                LogHelper.AddErrorLog(LastErrorMsg);
                return false;
            }
        }

        public bool WriteString(string variable, string val)
        {
            if (!_plc.IsConnected)
            {
                return false;
            }

            DataType dataType;
            int dbNumber;
            int address;
            try
            {
                VarType varType;
                int bitNumber;
                S7PlcProtocol.Parse(variable, out dataType, out dbNumber, out varType, out address, out bitNumber);
            }
            catch (Exception e)
            {
                LastErrorMsg = $@"变量名称[{variable}]解析失败：{e.Message}";
                LogHelper.AddErrorLog(LastErrorMsg);
                return false;
            }

            try
            {
                _plc.Write(dataType, dbNumber, address, (byte) val.Length);
                _plc.Write(dataType, dbNumber, address + 1, val);
                return true;
            }
            catch (Exception e)
            {
                LastErrorMsg = e.Message;
                LogHelper.AddErrorLog(LastErrorMsg);
                return false;
            }
        }

        public bool ReadS7String(string variable, out string val)
        {
            val = string.Empty;

            if (!_plc.IsConnected)
            {
                return false;
            }

            DataType dataType;
            int dbNumber;
            int address;
            try
            {
                VarType varType;
                int bitNumber;
                S7PlcProtocol.Parse(variable, out dataType, out dbNumber, out varType, out address, out bitNumber);
            }
            catch (Exception e)
            {
                LastErrorMsg = $@"变量名称[{variable}]解析失败：{e.Message}";
                LogHelper.AddErrorLog(LastErrorMsg);
                return false;
            }

            try
            {
                var reservedLength = (byte) _plc.Read(dataType, dbNumber, address, VarType.Byte, 1);
                val = (string) _plc.Read(dataType, dbNumber, address, VarType.S7String, reservedLength);
                return true;
            }
            catch (Exception e)
            {
                LastErrorMsg = e.Message;
                LogHelper.AddErrorLog(LastErrorMsg);
                return false;
            }
        }

        public bool WriteS7String(string variable, string val)
        {
            if (!_plc.IsConnected)
            {
                return false;
            }

            DataType dataType;
            int dbNumber;
            int address;
            try
            {
                VarType varType;
                int bitNumber;
                S7PlcProtocol.Parse(variable, out dataType, out dbNumber, out varType, out address, out bitNumber);
            }
            catch (Exception e)
            {
                LastErrorMsg = $@"变量名称[{variable}]解析失败：{e.Message}";
                LogHelper.AddErrorLog(LastErrorMsg);
                return false;
            }

            try
            {
                var temp = Encoding.ASCII.GetBytes(val);
                var bytes = S7.Net.Types.S7String.ToByteArray(val, temp.Length);
                _plc.WriteBytes(dataType, dbNumber, address, bytes);
                return true;
            }
            catch (Exception e)
            {
                LastErrorMsg = e.Message;
                LogHelper.AddErrorLog(LastErrorMsg);
                return false;
            }
        }

        #endregion
    }
}
